package com.ionehe.group.common.uaa.ex.domain.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import com.google.common.base.Throwables;
import com.ionehe.group.common.io.Response;
import com.ionehe.group.common.uaa.ex.domain.service.CustomerReadService;
import com.ionehe.group.common.uaa.ex.dto.CommonUserDetails;
import com.ionehe.group.common.uaa.ex.convert.FunctionConvert;
import com.ionehe.group.common.uaa.ex.application.converter.RoleInfoConvert;
import com.ionehe.group.common.uaa.ex.convert.UserInfoConvert;
import com.ionehe.group.common.uaa.ex.dto.CustomerInfoDTO;
import com.ionehe.group.common.uaa.ex.model.*;
import com.ionehe.group.common.uaa.ex.dto.FunctionDTO;
import com.ionehe.group.common.uaa.ex.dto.LoginInfoDTO;
import com.ionehe.group.common.uaa.ex.dto.Paging;
import com.ionehe.group.common.uaa.ex.dto.RoleInfoDTO;
import com.ionehe.group.common.uaa.ex.dto.UserDetailDTO;
import com.ionehe.group.common.uaa.ex.dto.UserInfoDTO;
import com.ionehe.group.common.uaa.ex.dto.UserInfoQueryDTO;
import com.ionehe.group.common.uaa.ex.enums.ErrorMessages;
import com.ionehe.group.common.uaa.ex.enums.UserType;
import com.ionehe.group.common.uaa.ex.exception.BusinessException;
import com.ionehe.group.common.uaa.ex.dao.*;
import com.ionehe.group.common.uaa.ex.domain.service.CustomerFunctionReadService;
import com.ionehe.group.common.uaa.ex.domain.service.UserCacheReadService;
import com.ionehe.group.common.uaa.ex.domain.service.UserCacheWriteService;
import com.ionehe.group.common.uaa.ex.domain.service.UserReadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @USER:秀
 * @DATE: 2020/11/30
 * @PROJECT_NAME: general-permissions
 * @Description 用户读服务类
 **/
@Service
@Slf4j
public class UserReadServiceImpl implements UserReadService {
    @Autowired
    private UserCacheReadService userCacheReadService;
    @Autowired
    private UserCacheWriteService userCacheWriteService;
    @Autowired
    private CustomerFunctionReadService customerFunctionReadService;
    @Resource
    private UserInfoDao userInfoDao;
    @Resource
    private LoginInfoDao loginInfoDao;
    @Resource
    private RoleInfoDao roleInfoDao;
    @Resource
    private UserRoleDao userRoleDao;
    @Resource
    private CustomerFunctionDao customerFunctionDao;
    @Resource
    private RoleFunctionDao roleFunctionDao;
    @Resource
    private UserInfoConvert userInfoConvert;
    @Resource
    private RoleInfoConvert roleInfoConvert;
    @Resource
    private FunctionConvert functionConvert;
    @Resource
    private FunctionDao functionDao;
    @Resource
    private CustomerSetMealDao customerSetMealDao;
    @Resource
    private CustomerReadService customerReadService;

    @Override
    public Response<UserDetails> loadUserByUsernameAndAccountType(String username, String type) {
        log.info("AdminService[]loadUserByUsername[]start! username:{}", username);
        try {
            //获取用户信息
            Response<UserDetailDTO> userDetailRes = getUserByUsername(username);
            if (!userDetailRes.success()) {
                return Response.no(userDetailRes.getMessage());
            }

            if (userDetailRes.getData() == null) {
                return Response.no("用户不存在！");
            }

            UserDetailDTO user = userDetailRes.getData();

            if (StrUtil.isNotBlank(type)) {
                String accountType = user.getCustomerInfoDTO().getType();
                if (!type.equals(accountType)) {
                    return Response.no("用户不存在！");
                }
            }

            if (setMealIslExpired(user)) {
                return Response.no(ErrorMessages.SETMEAL_IS_EXPIRED.toString());
            }

            user.getLoginInfoDTO().setAccount(user.getUserInfoDTO().getTerminal() + "_" + user.getLoginInfoDTO().getAccount());
            // 获取权限
            List<FunctionDTO> resourceDTOList = getResourceList(username).getData();
            return Response.yes(new CommonUserDetails(user.getLoginInfoDTO(), resourceDTOList));
        } catch (BusinessException e) {
            log.warn("AdminService[]loadUserByUsername[]warn! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("AdminService[]loadUserByUsername[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.GET_USER_INFO_FAIL.toString());
        }
    }

    @Override
    public Response<UserDetailDTO> getUserByUsername(String username) {
        log.info("UserInfoReadService[]getUserByUsername[]start! username:{}", username);
        try {
            // 缓存获取
            Response<UserDetailDTO> res = userCacheReadService.getUser(username);
            if (!res.success()) {
                return Response.no(res.getMessage());
            }
            if (res.getData() != null) {
                return Response.yes(res.getData());
            }

            // 数据库获取
            UserDetailDTO userDetailDTO = new UserDetailDTO();
            // 用户信息
            String[] s = username.split("_");
            UaaUserInfo user = new UaaUserInfo();
            user.setPhone(s[1]);
            user.setTerminal(Integer.parseInt(s[0]));
            user.setIsDelete(false);
            UaaUserInfo userInfo = userInfoDao.selectOne(user);
            if (userInfo == null) {
                return Response.yes(null);
            }
            // 公司信息
            Response<CustomerInfoDTO> customerInfoDTOResponse = customerReadService.detail(userInfo.getCustomerId());
            if (!customerInfoDTOResponse.success()) {
                log.warn("UserInfoReadService[]getUserByUsername[]get customer info error! cause:{}", customerInfoDTOResponse.getMessage());
            }
            userDetailDTO.setCustomerInfoDTO(customerInfoDTOResponse.getData());
            // 登陆信息
            UaaLoginInfo loginInfo = new UaaLoginInfo();
            loginInfo.setUserId(userInfo.getId());
            loginInfo.setIsDelete(false);
            loginInfo = loginInfoDao.selectOne(loginInfo);
            if (Objects.nonNull(loginInfo)) {
                LoginInfoDTO loginInfoDTO = userInfoConvert.do2dto(loginInfo);
                loginInfoDTO.setStatus(userInfo.getStatus());
                userDetailDTO.setLoginInfoDTO(loginInfoDTO);
                userDetailDTO.setUserInfoDTO(userInfoConvert.do2dto(userInfo));
                //userDetailDTO.setFunctionDetailDTOList(customerFunctionReadService.getFunctionTree(userInfoConvert.do2dto(userInfo)).getData());

                // 存入缓存中
                userCacheWriteService.setUser(userDetailDTO);
                return Response.yes(userDetailDTO);
            }
            return Response.yes(null);
        } catch (Exception e) {
            log.error("AdminReadService[]getAdminByUsername[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.GET_USER_INFO_FAIL.toString());
        }
    }

    @Override
    public Response<Paging<UserInfoDTO>> list(UserInfoQueryDTO queryDTO) {
        //查询用户信息列表
        Example example = new Example(UaaUserInfo.class);
        Example.Criteria criteria = example.createCriteria()
                .andEqualTo("customerId", queryDTO.getCustomerId())
                .andEqualTo("isDelete", false)
                .andEqualTo("type", UserType.NON_ADMIN.getValue());
        if (StrUtil.isNotBlank(queryDTO.getName())) {
            criteria.andLike("name", "%" + queryDTO.getName() + "%");
        }
        if (StrUtil.isNotBlank(queryDTO.getPhone())) {
            criteria.andLike("phone", "%" + queryDTO.getPhone() + "%");
        }
        if (Objects.nonNull(queryDTO.getCreateTimeStart())) {
            criteria.andBetween("createTime", queryDTO.getCreateTimeStart(), queryDTO.getCreateTimeEnd());
        }
        if (Objects.nonNull(queryDTO.getStatus())) {
            criteria.andEqualTo("status", queryDTO.getStatus());
        }
        Integer pageNum = queryDTO.getPageNum();
        Integer pageSize = queryDTO.getPageSize();
        pageNum = pageNum == null ? 1 : pageNum;
        pageSize = pageSize == null ? 10 : pageSize;
        Page<Object> page = PageMethod.startPage(pageNum, pageSize);
        List<UaaUserInfo> userInfoList = userInfoDao.selectByExample(example);
        if (CollectionUtils.isEmpty(userInfoList)) {
            return Response.yes(Paging.empty());
        }
        //组装用户id列表
        List<Long> userInfoIdList = userInfoList.stream().map(UaaUserInfo::getId).collect(Collectors.toList());

        //查询用户关联角色
        example = new Example(UaaUserRole.class);
        example.createCriteria().andIn("userId", userInfoIdList);
        List<UaaUserRole> userRoleList = userRoleDao.selectByExample(example);
        if (CollectionUtils.isEmpty(userRoleList)) {
            return Response.yes(new Paging<>(page.getTotal(), userInfoConvert.doList2dtoList(userInfoList)));
        }
        //组装roleId列表
        Set<Long> roleSet = new HashSet(userRoleList.size());
        //组装Map<userId，List<roleId>>
        Map<Long, List<Long>> userIdRoleIdMap = new HashMap();
        userRoleList.forEach(userRole -> {
            List<Long> roleIdList = userIdRoleIdMap.get(userRole.getUserId());
            if (CollectionUtils.isEmpty(roleIdList)) {
                roleIdList = new ArrayList();
                userIdRoleIdMap.put(userRole.getUserId(), roleIdList);
            }
            roleIdList.add(userRole.getRoleId());
            roleSet.add(userRole.getRoleId());
        });
        //查询所有角色
        List<UaaRoleInfo> roleInfoList = roleInfoDao.selectByIdList(new ArrayList(roleSet));
        Map<Long, UaaRoleInfo> roleInfoMap = roleInfoList.stream().collect(Collectors.toMap(UaaRoleInfo::getId, roleInfo -> roleInfo));
        List<Long> roleInfoIdList = roleInfoList.stream().map(UaaRoleInfo::getId).collect(Collectors.toList());
        //查询角色与功能关联
        example = new Example(UaaRoleFunction.class);
        example.createCriteria().andIn("roleId", roleInfoIdList);
        List<UaaRoleFunction> roleFunctionList = roleFunctionDao.selectByExample(example);
        //组装Map<roleId，List<functionId>>
        Map<Long, List<Long>> roleIdFunctionIdMap = new HashMap();
        roleFunctionList.forEach(roleFunction -> {
            List<Long> functionIdList = roleIdFunctionIdMap.get(roleFunction.getRoleId());
            if (CollectionUtils.isEmpty(functionIdList)) {
                functionIdList = new ArrayList();
                roleIdFunctionIdMap.put(roleFunction.getRoleId(), functionIdList);
            }
            functionIdList.add(roleFunction.getFunctionId());
        });
        return Response.yes(new Paging<>(page.getTotal(), bulidUserInfoDTO(userInfoList, userIdRoleIdMap, roleInfoMap, roleIdFunctionIdMap)));
    }

    /**
     * 组装用户信息
     *
     * @param userIdRoleIdMap
     * @param roleInfoMap
     * @param roleIdFunctionIdMap
     * @return
     */
    private List<UserInfoDTO> bulidUserInfoDTO(List<UaaUserInfo> userInfoList, Map<Long, List<Long>> userIdRoleIdMap, Map<Long, UaaRoleInfo> roleInfoMap, Map<Long, List<Long>> roleIdFunctionIdMap) {
        List<UserInfoDTO> userInfoDTOList = new ArrayList(userInfoList.size());
        userInfoList.forEach(userInfo -> {
            UserInfoDTO userInfoDTO = userInfoConvert.do2dto(userInfo);
            List<Long> roleIdList = userIdRoleIdMap.get(userInfoDTO.getId());
            if (!CollectionUtils.isEmpty(roleIdList)) {
                List<RoleInfoDTO> roleInfoDTOList = new ArrayList(roleIdList.size());
                roleIdList.forEach(roleId -> {
                    UaaRoleInfo roleInfo = roleInfoMap.get(roleId);
                    RoleInfoDTO roleInfoDTO = roleInfoConvert.do2dto(roleInfo);
                    roleInfoDTOList.add(roleInfoDTO);
                });
                userInfoDTO.setRoleInfoDTOList(roleInfoDTOList);
            }
            List<Long> functionIdList = getFunctionIdListByRoleIdList(roleIdList, roleIdFunctionIdMap);
            userInfoDTO.setFunctionIds(functionIdList);
            userInfoDTOList.add(userInfoDTO);
        });
        return userInfoDTOList;
    }

    /**
     * 根据角色id列表获取功能并集
     *
     * @param roleIdList
     * @return
     */
    private List<Long> getFunctionIdListByRoleIdList(List<Long> roleIdList, Map<Long, List<Long>> roleIdFunctionIdMap) {
        if (CollectionUtils.isEmpty(roleIdList)) {
            return new ArrayList();
        }
        Set<Long> functionIdList = new HashSet();
        roleIdList.forEach(roleId -> {
            List<Long> functionIds = roleIdFunctionIdMap.get(roleId);
            if (!CollectionUtils.isEmpty(functionIds)) {
                functionIdList.addAll(functionIds);
            }
        });
        return new ArrayList(functionIdList);
    }

    /**
     * 根据用户id获取功能列表
     *
     * @param userInfo
     * @return
     */
    private List<FunctionDTO> getFunctionByUserId(UaaUserInfo userInfo) {
        List<UaaFunction> functionList;
        if (Objects.equals(UserType.ADMIN.getValue(), userInfo.getType())) {
            UaaCustomerFunction customerFunctionQuery = new UaaCustomerFunction();
            customerFunctionQuery.setCustomerId(userInfo.getCustomerId());
            List<UaaCustomerFunction> customerFunctionList = customerFunctionDao.select(customerFunctionQuery);
            if (CollectionUtils.isEmpty(customerFunctionList)) {
                return new ArrayList();
            }
            List<Long> functionIdList = customerFunctionList.stream().map(UaaCustomerFunction::getFunctionId).collect(Collectors.toList());
            functionList = functionDao.selectByIdList(functionIdList);

        } else {
            //查询出所有用户关联角色
            UaaUserRole userRoleQuery = new UaaUserRole();
            userRoleQuery.setUserId(userInfo.getId());
            List<UaaUserRole> userRoleList = userRoleDao.select(userRoleQuery);
            if (CollectionUtils.isEmpty(userRoleList)) {
                return new ArrayList();
            }
            //组装角色ID列表
            List<Long> roleIdList = userRoleList.stream().map(UaaUserRole::getRoleId).collect(Collectors.toList());
            //查询角色与功能关联表
            Example example = new Example(UaaRoleFunction.class);
            example.createCriteria().andIn("roleId", roleIdList);
            List<UaaRoleFunction> roleFunctionList = roleFunctionDao.selectByExample(example);
            if (CollectionUtils.isEmpty(roleFunctionList)) {
                return new ArrayList();
            }
            //组装功能idset
            Set<Long> functionIdSet = roleFunctionList.stream().map(UaaRoleFunction::getFunctionId).collect(Collectors.toSet());
            functionList = functionDao.selectByIdList(new ArrayList(functionIdSet));
        }
        return functionConvert.do2dto(functionList);
    }

    /**
     * 获取指定用户的可访问资源
     *
     * @param account 用户账号
     * @return 用户的可访问资源
     */
    @Override
    public Response<List<FunctionDTO>> getResourceList(String account) {
        // 缓存中获取
        Response<List<FunctionDTO>> resourceListCacheRes = userCacheReadService.getResourceList(account);
        if (!resourceListCacheRes.success()) {
            throw new BusinessException(resourceListCacheRes.getMessage());
        }
        List<FunctionDTO> resourceList = resourceListCacheRes.getData();

        // 数据库中获取
        if (CollUtil.isEmpty(resourceList)) {
            String phone = "";
            int terminal = 0;
            if (account.contains("_")) {
                String[] s = account.split("_");
                terminal = Integer.parseInt(s[0]);
                phone = s[1];
            }
            UaaUserInfo user = new UaaUserInfo();
            user.setPhone(phone);
            user.setTerminal(terminal);
            user.setIsDelete(false);
            user = userInfoDao.selectOne(user);
            if (Objects.isNull(user)) {
                return Response.no(ErrorMessages.ILLEGAL_OPERATION.getErrCode(), ErrorMessages.ILLEGAL_OPERATION.getErrMsg());
            }
            resourceList = getFunctionByUserId(user);
            // 存入缓存中
            userCacheWriteService.setResourceList(account, resourceList);
        }
        return Response.yes(resourceList);
    }


    private boolean setMealIslExpired(UserDetailDTO user) {
        if (Objects.isNull(user)) {
            throw new BusinessException(ErrorMessages.USER_IS_NOT_EXIST.toString());
        }
        Long customerId = user.getUserInfoDTO().getCustomerId();
        Example example = new Example(UaaCustomerSetMeal.class);
        example.createCriteria().andEqualTo("customerId", customerId);
        // 查找该客户所持有的套餐
        List<UaaCustomerSetMeal> customerSetMeals = customerSetMealDao.selectByExample(example);
        // 获取套餐超时时间
        ArrayList<Date> currentSetMealExpirationTimeList = new ArrayList<>();
        for (UaaCustomerSetMeal customerSetMeal : customerSetMeals) {
            currentSetMealExpirationTimeList.add(customerSetMeal.getTimeout());
        }
        for (Date date : currentSetMealExpirationTimeList) {
            if (date.before(new Date()) | date.equals(new Date())) {
                return true;
            }
        }
        return false;
    }
}
